import contextlib

from asgiref.local import Local
from django.core.serializers.json import DjangoJSONEncoder
from django.db import models

from sysreptor.users.models import PentestUser
from sysreptor.utils.crypto.fields import EncryptedField
from sysreptor.utils.models import BaseModel


class CollabEventType(models.TextChoices):
    CONNECT = 'collab.connect', 'Connect'
    DISCONNECT = 'collab.disconnect', 'Disconnect'
    INIT = 'collab.init', 'Init'
    CREATE = 'collab.create', 'Create'
    UPDATE_KEY = 'collab.update_key', 'Update Key'
    UPDATE_TEXT = 'collab.update_text', 'Update Text'
    UPDATE_EXCALIDRAW = 'collab.update_excalidraw', 'Update Excalidraw'
    DELETE = 'collab.delete', 'Delete'
    SORT = 'collab.sort', 'Sort'
    AWARENESS = 'collab.awareness', 'Awareness'


class CollabEvent(BaseModel):
    related_id = models.UUIDField(db_index=True)
    path = models.TextField(db_index=True)
    type = models.CharField(choices=CollabEventType.choices, max_length=30, db_index=True)
    client_id = models.CharField(max_length=100, null=True, blank=True)
    version = models.FloatField(db_index=True)
    data = EncryptedField(base_field=models.JSONField(encoder=DjangoJSONEncoder, default=dict))

    def to_dict(self):
        return {
            "type": self.type,
            "path": self.path,
            "client_id": self.client_id,
            "version": self.version,
            **self.data,
        }


class CollabClientInfo(BaseModel):
    related_id = models.UUIDField(db_index=True)
    user = models.ForeignKey(PentestUser, on_delete=models.CASCADE, null=True, blank=True)
    client_id = models.CharField(max_length=100, unique=True, db_index=True)
    client_color = models.CharField(max_length=10)
    path = models.TextField(null=True, blank=True)


collab_context_store = Local()


@contextlib.contextmanager
def collab_context(**kwargs):
    restore_map = {}
    try:
        for k, v in kwargs.items():
            if hasattr(collab_context_store, k):
                restore_map[k] = getattr(collab_context_store, k)
            setattr(collab_context_store, k, v)

        yield
    finally:
        for k in kwargs.keys():
            if k in restore_map:
                setattr(collab_context_store, k, restore_map[k])
            else:
                delattr(collab_context_store, k)

